line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package PDF::Builder::Annotation; |
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
1641
|
use base 'PDF::Builder::Basic::PDF::Dict'; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
211
|
|
4
|
|
|
|
|
|
|
|
5
|
2
|
|
|
2
|
|
13
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
50
|
|
6
|
2
|
|
|
2
|
|
13
|
use warnings; |
|
2
|
|
|
|
|
76
|
|
|
2
|
|
|
|
|
140
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '3.025'; # VERSION |
9
|
|
|
|
|
|
|
our $LAST_UPDATE = '3.024'; # manually update whenever code is changed |
10
|
|
|
|
|
|
|
|
11
|
2
|
|
|
2
|
|
14
|
use PDF::Builder::Basic::PDF::Utils; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
183
|
|
12
|
2
|
|
|
2
|
|
15
|
use List::Util qw(min max); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
145
|
|
13
|
2
|
|
|
2
|
|
13
|
use Carp; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
9553
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 NAME |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
PDF::Builder::Annotation - Add annotations to a PDF |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 SYNOPSIS |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my $pdf = PDF::Builder->new(); |
22
|
|
|
|
|
|
|
my $font = $pdf->font('Helvetica'); |
23
|
|
|
|
|
|
|
my $page1 = $pdf->page(); |
24
|
|
|
|
|
|
|
my $page2 = $pdf->page(); |
25
|
|
|
|
|
|
|
my $content = $page1->text(); |
26
|
|
|
|
|
|
|
my $message = 'Go to Page 2'; |
27
|
|
|
|
|
|
|
my $size = 18; |
28
|
|
|
|
|
|
|
$content->distance(1 * 72, 9 * 72); |
29
|
|
|
|
|
|
|
$content->font($font, $size); |
30
|
|
|
|
|
|
|
$content->text($message); |
31
|
|
|
|
|
|
|
my $annotation = $page1->annotation(); |
32
|
|
|
|
|
|
|
my $width = $content->text_width($message); |
33
|
|
|
|
|
|
|
$annotation->rect(1 * 72, 9 * 72, 1 * 72 + $width, 9 * 72 + $size); |
34
|
|
|
|
|
|
|
$annotation->link($page2); |
35
|
|
|
|
|
|
|
$pdf->save('sample.pdf'); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=head1 METHODS |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
Note that the handling of annotations can vary from Reader to Reader. The |
40
|
|
|
|
|
|
|
available icon set may be larger or smaller than given here, and some Readers |
41
|
|
|
|
|
|
|
activate an annotation on a single mouse click, while others require a double |
42
|
|
|
|
|
|
|
click. Not all features provided here may be available on all PDF Readers. |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=over |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=item $annotation = PDF::Builder::Annotation->new() |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
Returns an annotation object (called from $page->annotation()). |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
It is normally I necessary to explicitly call this method (see examples). |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=cut |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# %opts removed, as there are currently none |
55
|
|
|
|
|
|
|
sub new { |
56
|
6
|
|
|
6
|
1
|
15
|
my ($class) = @_; |
57
|
|
|
|
|
|
|
|
58
|
6
|
|
|
|
|
24
|
my $self = $class->SUPER::new(); |
59
|
6
|
|
|
|
|
17
|
$self->{'Type'} = PDFName('Annot'); |
60
|
6
|
|
|
|
|
18
|
$self->{'Border'} = PDFArray(PDFNum(0), PDFNum(0), PDFNum(0)); # no border |
61
|
|
|
|
|
|
|
|
62
|
6
|
|
|
|
|
17
|
return $self; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
#sub outobjdeep { |
66
|
|
|
|
|
|
|
# my ($self, @opts) = @_; |
67
|
|
|
|
|
|
|
# |
68
|
|
|
|
|
|
|
# foreach my $k (qw[ api apipdf apipage ]) { |
69
|
|
|
|
|
|
|
# $self->{" $k"} = undef; |
70
|
|
|
|
|
|
|
# delete($self->{" $k"}); |
71
|
|
|
|
|
|
|
# } |
72
|
|
|
|
|
|
|
# return $self->SUPER::outobjdeep(@opts); |
73
|
|
|
|
|
|
|
#} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# ============== start of annotation types ======================= |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# note that %opts is given as the only format in most cases, as rect |
78
|
|
|
|
|
|
|
# is a mandatory "option" |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=back |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=head2 Annotation types |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=over |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=item $annotation->link($page, %opts) |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Defines the annotation as a launch-page with page C<$page> (within I |
89
|
|
|
|
|
|
|
document) and opts %opts (rect, border, color, I: see |
90
|
|
|
|
|
|
|
descriptions below). |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
B that C<$page> is I a simple page number, but is a page structure |
93
|
|
|
|
|
|
|
such as C<$pdf-Eopenpage(page_number)>, I a Named Destination defined |
94
|
|
|
|
|
|
|
elsewhere. |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=cut |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# consider goto() as alias, for consistency with NamedDestination |
99
|
|
|
|
|
|
|
#sub goto { return link(@_); } ## no critic |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
sub link { |
102
|
1
|
|
|
1
|
1
|
6
|
my ($self, $page, %opts) = @_; |
103
|
|
|
|
|
|
|
# copy dashed names over to preferred non-dashed names |
104
|
1
|
50
|
33
|
|
|
5
|
if (defined $opts{'-rect'} && !defined $opts{'rect'}) { $opts{'rect'} = delete($opts{'-rect'}); } |
|
0
|
|
|
|
|
0
|
|
105
|
1
|
50
|
33
|
|
|
5
|
if (defined $opts{'-border'} && !defined $opts{'border'}) { $opts{'border'} = delete($opts{'-border'}); } |
|
0
|
|
|
|
|
0
|
|
106
|
1
|
50
|
33
|
|
|
4
|
if (defined $opts{'-color'} && !defined $opts{'color'}) { $opts{'color'} = delete($opts{'-color'}); } |
|
0
|
|
|
|
|
0
|
|
107
|
|
|
|
|
|
|
|
108
|
1
|
|
|
|
|
4
|
$self->{'Subtype'} = PDFName('Link'); |
109
|
1
|
50
|
|
|
|
17
|
if (ref($page)) { |
110
|
|
|
|
|
|
|
# page structure |
111
|
1
|
|
|
|
|
5
|
$self->{'A'} = PDFDict(); |
112
|
1
|
|
|
|
|
5
|
$self->{'A'}->{'S'} = PDFName('GoTo'); |
113
|
|
|
|
|
|
|
} else { |
114
|
|
|
|
|
|
|
# named destination |
115
|
0
|
|
|
|
|
0
|
$self->{'Dest'} = PDFString($page, 'n'); |
116
|
|
|
|
|
|
|
# PDF::API2 returns $self at this point! |
117
|
|
|
|
|
|
|
} |
118
|
1
|
|
|
|
|
8
|
$self->dest($page, %opts); |
119
|
1
|
50
|
|
|
|
4
|
$self->rect(@{$opts{'rect'}}) if defined $opts{'rect'}; |
|
0
|
|
|
|
|
0
|
|
120
|
1
|
50
|
|
|
|
13
|
$self->border(@{$opts{'border'}}) if defined $opts{'border'}; |
|
0
|
|
|
|
|
0
|
|
121
|
1
|
50
|
|
|
|
4
|
$self->Color(@{$opts{'color'}}) if defined $opts{'color'}; |
|
0
|
|
|
|
|
0
|
|
122
|
|
|
|
|
|
|
|
123
|
1
|
|
|
|
|
3
|
return $self; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=item $annotation->pdf($pdffile, $page_number, %opts) |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
Defines the annotation as a PDF-file with filepath C<$pdffile>, on page |
129
|
|
|
|
|
|
|
C<$page_number>, and opts %opts (rect, border, color, I: see |
130
|
|
|
|
|
|
|
descriptions below). This differs from the C call in that the target |
131
|
|
|
|
|
|
|
is found in a different PDF file, not the current document. |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
C<$page_number> is the physical page number, starting at 1: 1, 2,... |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
B C and C |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
Originally this method was named C, and then C but a recent |
138
|
|
|
|
|
|
|
PDF::API2 change made it C. For compatibility, it has been changed to |
139
|
|
|
|
|
|
|
C, with C and C still available as aliases. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=cut |
142
|
|
|
|
|
|
|
|
143
|
0
|
|
|
0
|
0
|
0
|
sub pdfile { return pdf(@_); } ## no critic |
144
|
0
|
|
|
0
|
0
|
0
|
sub pdf_file { return pdf(@_); } ## no critic |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
sub pdf { |
147
|
1
|
|
|
1
|
1
|
17
|
my ($self, $url, $page_number, %opts) = @_; |
148
|
|
|
|
|
|
|
# note that although "url" is used, it may be a local file |
149
|
|
|
|
|
|
|
# copy dashed names over to preferred non-dashed names |
150
|
1
|
50
|
33
|
|
|
18
|
if (defined $opts{'-rect'} && !defined $opts{'rect'}) { $opts{'rect'} = delete($opts{'-rect'}); } |
|
0
|
|
|
|
|
0
|
|
151
|
1
|
50
|
33
|
|
|
6
|
if (defined $opts{'-color'} && !defined $opts{'color'}) { $opts{'color'} = delete($opts{'-color'}); } |
|
0
|
|
|
|
|
0
|
|
152
|
1
|
50
|
33
|
|
|
5
|
if (defined $opts{'-border'} && !defined $opts{'border'}) { $opts{'border'} = delete($opts{'-border'}); } |
|
0
|
|
|
|
|
0
|
|
153
|
|
|
|
|
|
|
|
154
|
1
|
|
|
|
|
4
|
$self->{'Subtype'} = PDFName('Link'); |
155
|
1
|
|
|
|
|
4
|
$self->{'A'} = PDFDict(); |
156
|
1
|
|
|
|
|
5
|
$self->{'A'}->{'S'} = PDFName('GoToR'); |
157
|
1
|
|
|
|
|
6
|
$self->{'A'}->{'F'} = PDFString($url, 'u'); |
158
|
|
|
|
|
|
|
|
159
|
1
|
|
|
|
|
3
|
$page_number--; # wants it numbered starting at 0 |
160
|
1
|
|
|
|
|
13
|
$self->dest(PDFNum($page_number), %opts); |
161
|
1
|
50
|
|
|
|
7
|
$self->rect(@{$opts{'rect'}}) if defined $opts{'rect'}; |
|
0
|
|
|
|
|
0
|
|
162
|
1
|
50
|
|
|
|
5
|
$self->Color(@{$opts{'color'}}) if defined $opts{'color'}; |
|
0
|
|
|
|
|
0
|
|
163
|
1
|
50
|
|
|
|
4
|
$self->border(@{$opts{'border'}}) if defined $opts{'border'}; |
|
0
|
|
|
|
|
0
|
|
164
|
|
|
|
|
|
|
|
165
|
1
|
|
|
|
|
7
|
return $self; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=item $annotation->launch($file, %opts) |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
Defines the annotation as a launch-file with filepath C<$file> (a local file) |
171
|
|
|
|
|
|
|
and options %opts (rect, border, color: see descriptions below). |
172
|
|
|
|
|
|
|
I the file is displayed depends on the operating system, type of file, |
173
|
|
|
|
|
|
|
and local configuration or mapping. |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
B C |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Originally this method was named C, but a recent PDF::API2 change made it |
178
|
|
|
|
|
|
|
C. For compatibility, it has been changed to C, with C |
179
|
|
|
|
|
|
|
still available as an alias. |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=cut |
182
|
|
|
|
|
|
|
|
183
|
0
|
|
|
0
|
0
|
0
|
sub file { return launch(@_); } ## no critic |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub launch { |
186
|
1
|
|
|
1
|
1
|
11
|
my ($self, $file, %opts) = @_; |
187
|
|
|
|
|
|
|
# copy dashed names over to preferred non-dashed names |
188
|
1
|
50
|
33
|
|
|
6
|
if (defined $opts{'-rect'} && !defined $opts{'rect'}) { $opts{'rect'} = delete($opts{'-rect'}); } |
|
0
|
|
|
|
|
0
|
|
189
|
1
|
50
|
33
|
|
|
4
|
if (defined $opts{'-color'} && !defined $opts{'color'}) { $opts{'color'} = delete($opts{'-color'}); } |
|
0
|
|
|
|
|
0
|
|
190
|
1
|
50
|
33
|
|
|
5
|
if (defined $opts{'-border'} && !defined $opts{'border'}) { $opts{'border'} = delete($opts{'-border'}); } |
|
0
|
|
|
|
|
0
|
|
191
|
|
|
|
|
|
|
|
192
|
1
|
|
|
|
|
3
|
$self->{'Subtype'} = PDFName('Link'); |
193
|
1
|
|
|
|
|
4
|
$self->{'A'} = PDFDict(); |
194
|
1
|
|
|
|
|
4
|
$self->{'A'}->{'S'} = PDFName('Launch'); |
195
|
1
|
|
|
|
|
3
|
$self->{'A'}->{'F'} = PDFString($file, 'f'); |
196
|
|
|
|
|
|
|
|
197
|
1
|
50
|
|
|
|
7
|
$self->rect(@{$opts{'rect'}}) if defined $opts{'rect'}; |
|
0
|
|
|
|
|
0
|
|
198
|
1
|
50
|
|
|
|
6
|
$self->Color(@{$opts{'color'}}) if defined $opts{'color'}; |
|
0
|
|
|
|
|
0
|
|
199
|
1
|
50
|
|
|
|
4
|
$self->border(@{$opts{'border'}}) if defined $opts{'border'}; |
|
0
|
|
|
|
|
0
|
|
200
|
|
|
|
|
|
|
|
201
|
1
|
|
|
|
|
2
|
return $self; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=item $annotation->uri($url, %opts) |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
Defines the annotation as a launch-url with url C<$url> and |
207
|
|
|
|
|
|
|
options %opts (rect, border, color: see descriptions below). |
208
|
|
|
|
|
|
|
This page is usually brought up in a browser, and may be remote. |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
B C |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
Originally this method was named C, but a recent PDF::API2 change made it |
213
|
|
|
|
|
|
|
C. For compatibility, it has been changed to C, with C still |
214
|
|
|
|
|
|
|
available as an alias. |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=cut |
217
|
|
|
|
|
|
|
|
218
|
0
|
|
|
0
|
0
|
0
|
sub url { return uri(@_); } ## no critic |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
sub uri { |
221
|
1
|
|
|
1
|
1
|
7
|
my ($self, $url, %opts) = @_; |
222
|
|
|
|
|
|
|
# copy dashed names over to preferred non-dashed names |
223
|
1
|
50
|
33
|
|
|
7
|
if (defined $opts{'-rect'} && !defined $opts{'rect'}) { $opts{'rect'} = delete($opts{'-rect'}); } |
|
0
|
|
|
|
|
0
|
|
224
|
1
|
50
|
33
|
|
|
7
|
if (defined $opts{'-color'} && !defined $opts{'color'}) { $opts{'color'} = delete($opts{'-color'}); } |
|
0
|
|
|
|
|
0
|
|
225
|
1
|
50
|
33
|
|
|
5
|
if (defined $opts{'-border'} && !defined $opts{'border'}) { $opts{'border'} = delete($opts{'-border'}); } |
|
0
|
|
|
|
|
0
|
|
226
|
|
|
|
|
|
|
|
227
|
1
|
|
|
|
|
4
|
$self->{'Subtype'} = PDFName('Link'); |
228
|
1
|
|
|
|
|
4
|
$self->{'A'} = PDFDict(); |
229
|
1
|
|
|
|
|
4
|
$self->{'A'}->{'S'} = PDFName('URI'); |
230
|
1
|
|
|
|
|
3
|
$self->{'A'}->{'URI'} = PDFString($url, 'u'); |
231
|
|
|
|
|
|
|
|
232
|
1
|
50
|
|
|
|
5
|
$self->rect(@{$opts{'rect'}}) if defined $opts{'rect'}; |
|
0
|
|
|
|
|
0
|
|
233
|
1
|
50
|
|
|
|
5
|
$self->Color(@{$opts{'color'}}) if defined $opts{'color'}; |
|
0
|
|
|
|
|
0
|
|
234
|
1
|
50
|
|
|
|
3
|
$self->border(@{$opts{'border'}}) if defined $opts{'border'}; |
|
0
|
|
|
|
|
0
|
|
235
|
|
|
|
|
|
|
|
236
|
1
|
|
|
|
|
3
|
return $self; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=item $annotation->text($text, %opts) |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
Defines the annotation as a text note with content string C<$text> and |
242
|
|
|
|
|
|
|
options %opts (rect, color, text, open: see descriptions below). |
243
|
|
|
|
|
|
|
The C<$text> may include newlines \n for multiple lines. The option border is |
244
|
|
|
|
|
|
|
ignored, since an I is used. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
The option C is the popup's label string, not to be confused with the |
247
|
|
|
|
|
|
|
main C<$text>. |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
The icon appears in the upper left corner of the C selection rectangle, |
250
|
|
|
|
|
|
|
and its active clickable area is fixed by the icon (it is I equal to the |
251
|
|
|
|
|
|
|
rectangle). The icon size is fixed, and its fill color set by C. |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
Additional options: |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
=over |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
=item icon => name_string |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=item icon => reference |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Specify the B to be used. The default is Reader-specific (usually |
262
|
|
|
|
|
|
|
C), and others may be |
263
|
|
|
|
|
|
|
defined by the Reader. C, C, C, C, |
264
|
|
|
|
|
|
|
C, and C are also supposed to |
265
|
|
|
|
|
|
|
be available on all PDF Readers. Note that the name I must exactly match. |
266
|
|
|
|
|
|
|
The icon is of fixed size. |
267
|
|
|
|
|
|
|
Any I dictionary entry will override the icon setting. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
A I to an icon may be passed instead of a name. |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=item opacity => I |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
Define the opacity (non-transparency, opaqueness) of the icon. This value |
274
|
|
|
|
|
|
|
ranges from 0.0 (transparent) to 1.0 (fully opaque), and applies to both |
275
|
|
|
|
|
|
|
the outline and the fill color. The default is 1.0. |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=back |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=cut |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# the icon size appears to be fixed. the last font size used does not affect it |
282
|
|
|
|
|
|
|
# and enabling icon_appearance() for it doesn't seem to do anything |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
sub text { |
285
|
2
|
|
|
2
|
1
|
20
|
my ($self, $text, %opts) = @_; |
286
|
|
|
|
|
|
|
# copy dashed names over to preferred non-dashed names |
287
|
2
|
50
|
33
|
|
|
8
|
if (defined $opts{'-rect'} && !defined $opts{'rect'}) { $opts{'rect'} = delete($opts{'-rect'}); } |
|
0
|
|
|
|
|
0
|
|
288
|
2
|
50
|
33
|
|
|
8
|
if (defined $opts{'-color'} && !defined $opts{'color'}) { $opts{'color'} = delete($opts{'-color'}); } |
|
0
|
|
|
|
|
0
|
|
289
|
2
|
50
|
33
|
|
|
7
|
if (defined $opts{'-border'} && !defined $opts{'border'}) { $opts{'border'} = delete($opts{'-border'}); } |
|
0
|
|
|
|
|
0
|
|
290
|
2
|
50
|
33
|
|
|
7
|
if (defined $opts{'-open'} && !defined $opts{'open'}) { $opts{'open'} = delete($opts{'-open'}); } |
|
0
|
|
|
|
|
0
|
|
291
|
2
|
50
|
33
|
|
|
9
|
if (defined $opts{'-text'} && !defined $opts{'text'}) { $opts{'text'} = delete($opts{'-text'}); } |
|
0
|
|
|
|
|
0
|
|
292
|
2
|
50
|
33
|
|
|
9
|
if (defined $opts{'-opacity'} && !defined $opts{'opacity'}) { $opts{'opacity'} = delete($opts{'-opacity'}); } |
|
0
|
|
|
|
|
0
|
|
293
|
2
|
50
|
33
|
|
|
6
|
if (defined $opts{'-icon'} && !defined $opts{'icon'}) { $opts{'icon'} = delete($opts{'-icon'}); } |
|
0
|
|
|
|
|
0
|
|
294
|
|
|
|
|
|
|
|
295
|
2
|
|
|
|
|
6
|
$self->{'Subtype'} = PDFName('Text'); |
296
|
2
|
|
|
|
|
9
|
$self->content($text); |
297
|
|
|
|
|
|
|
|
298
|
2
|
50
|
|
|
|
7
|
$self->rect(@{$opts{'rect'}}) if defined $opts{'rect'}; |
|
2
|
|
|
|
|
8
|
|
299
|
2
|
50
|
|
|
|
6
|
$self->Color(@{$opts{'color'}}) if defined $opts{'color'}; |
|
0
|
|
|
|
|
0
|
|
300
|
|
|
|
|
|
|
#$self->border($opts{'border'}) if defined $opts{'border'}; # ignored |
301
|
2
|
50
|
|
|
|
6
|
$self->open($opts{'open'}) if defined $opts{'open'}; |
302
|
|
|
|
|
|
|
# popup label (title) |
303
|
|
|
|
|
|
|
# have seen /T as (xFEFF UTF-16 chars) |
304
|
2
|
50
|
|
|
|
7
|
$self->{'T'} = PDFString($opts{'text'}, 'p') if exists $opts{'text'}; |
305
|
|
|
|
|
|
|
# icon opacity? |
306
|
2
|
50
|
|
|
|
15
|
if (defined $opts{'opacity'}) { |
307
|
0
|
|
|
|
|
0
|
$self->{'CA'} = PDFNum($opts{'opacity'}); |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
# Icon Name will be ignored if there is an AP. |
311
|
2
|
|
|
|
|
5
|
my $icon; # perlcritic doesn't want 2 lines combined |
312
|
2
|
50
|
|
|
|
5
|
$icon = $opts{'icon'} if exists $opts{'icon'}; |
313
|
2
|
50
|
33
|
|
|
6
|
$self->{'Name'} = PDFName($icon) if $icon && !ref($icon); # icon name |
314
|
|
|
|
|
|
|
# Set the icon appearance |
315
|
2
|
50
|
|
|
|
5
|
$self->icon_appearance($icon, %opts) if $icon; |
316
|
|
|
|
|
|
|
|
317
|
2
|
|
|
|
|
7
|
return $self; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=item $annotation->markup($text, $PointList, $highlight, %opts) |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
Defines the annotation as a text note with content string C<$text> and |
323
|
|
|
|
|
|
|
options %opts (color, text, open, opacity: see descriptions below). |
324
|
|
|
|
|
|
|
The C<$text> may include newlines \n for multiple lines. |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
C is the popup's label string, not to be confused with the main C<$text>. |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
There is no icon. Instead, the annotated text marked by C<$PointList> is |
329
|
|
|
|
|
|
|
highlighted in one of four ways specified by C<$highlight>. |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=over |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=item $PointList => [ 8n numbers ] |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
One or more sets of numeric coordinates are given, defining the quadrilateral |
336
|
|
|
|
|
|
|
(usually a rectangle) around the text to be highlighted and selectable |
337
|
|
|
|
|
|
|
(clickable, to bring up the annotation text). These |
338
|
|
|
|
|
|
|
are four sets of C coordinates, given (for Left-to-Right text) as the |
339
|
|
|
|
|
|
|
upper bound Upper Left to Upper Right and then the lower bound Lower Left to |
340
|
|
|
|
|
|
|
Lower Right. B
|
341
|
|
|
|
|
|
|
documented in the PDF specification!> It is important that the coordinates be |
342
|
|
|
|
|
|
|
given in this order. |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
Multiple sets of quadrilateral corners may be given, such as for highlighted |
345
|
|
|
|
|
|
|
text that wraps around to new line(s). The minimum is one set (8 numbers). |
346
|
|
|
|
|
|
|
Any I dictionary entry will override the C<$PointList> setting. Finally, |
347
|
|
|
|
|
|
|
the "Rect" selection rectangle is created I the convex bounding |
348
|
|
|
|
|
|
|
box defined by C<$PointList>. |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=item $highlight => 'string' |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
The following highlighting effects are permitted. The C must be |
353
|
|
|
|
|
|
|
spelled and capitalized I as given: |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=over |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=item Highlight |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
The effect of a translucent "highlighter" marker. |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=item Squiggly |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
The effect is an underline written in a "squiggly" manner. |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=item StrikeOut |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
The text is struck-through with a straight line. |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=item Underline |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
The text is marked by a straight underline. |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
=back |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=item color => I |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
If C is not given (an array of numbers in the range 0.0-1.0), a |
378
|
|
|
|
|
|
|
medium gray should be used by default. |
379
|
|
|
|
|
|
|
Named colors are not supported at this time. |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=item opacity => I |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
Define the opacity (non-transparency, opaqueness) of the icon. This value |
384
|
|
|
|
|
|
|
ranges from 0.0 (transparent) to 1.0 (fully opaque), and applies to both |
385
|
|
|
|
|
|
|
the outline and the fill color. The default is 1.0. |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
=back |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
=cut |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
sub markup { |
392
|
0
|
|
|
0
|
1
|
0
|
my ($self, $text, $PointList, $highlight, %opts) = @_; |
393
|
|
|
|
|
|
|
# copy dashed names over to preferred non-dashed names |
394
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-color'} && !defined $opts{'color'}) { $opts{'color'} = delete($opts{'-color'}); } |
|
0
|
|
|
|
|
0
|
|
395
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-open'} && !defined $opts{'open'}) { $opts{'open'} = delete($opts{'-open'}); } |
|
0
|
|
|
|
|
0
|
|
396
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-text'} && !defined $opts{'text'}) { $opts{'text'} = delete($opts{'-text'}); } |
|
0
|
|
|
|
|
0
|
|
397
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-opacity'} && !defined $opts{'opacity'}) { $opts{'opacity'} = delete($opts{'-opacity'}); } |
|
0
|
|
|
|
|
0
|
|
398
|
|
|
|
|
|
|
|
399
|
0
|
|
|
|
|
0
|
my @pointList = @{ $PointList }; |
|
0
|
|
|
|
|
0
|
|
400
|
0
|
0
|
0
|
|
|
0
|
if ((scalar @pointList) == 0 || (scalar @pointList)%8) { |
401
|
0
|
|
|
|
|
0
|
die "markup point list does not have 8*N entries!\n"; |
402
|
|
|
|
|
|
|
} |
403
|
0
|
|
|
|
|
0
|
$self->{'Subtype'} = PDFName($highlight); |
404
|
0
|
|
|
|
|
0
|
delete $self->{'Border'}; |
405
|
0
|
|
|
|
|
0
|
$self->{'QuadPoints'} = PDFArray(map {PDFNum($_)} @pointList); |
|
0
|
|
|
|
|
0
|
|
406
|
0
|
|
|
|
|
0
|
$self->content($text); |
407
|
|
|
|
|
|
|
|
408
|
0
|
|
|
|
|
0
|
my $minX = min($pointList[0], $pointList[2], $pointList[4], $pointList[6]); |
409
|
0
|
|
|
|
|
0
|
my $maxX = max($pointList[0], $pointList[2], $pointList[4], $pointList[6]); |
410
|
0
|
|
|
|
|
0
|
my $minY = min($pointList[1], $pointList[3], $pointList[5], $pointList[7]); |
411
|
0
|
|
|
|
|
0
|
my $maxY = max($pointList[1], $pointList[3], $pointList[5], $pointList[7]); |
412
|
0
|
|
|
|
|
0
|
$self->rect($minX-.5,$minY-.5, $maxX+.5,$maxY+.5); |
413
|
|
|
|
|
|
|
|
414
|
0
|
0
|
|
|
|
0
|
$self->open($opts{'open'}) if defined $opts{'open'}; |
415
|
0
|
0
|
|
|
|
0
|
if (defined $opts{'color'}) { |
416
|
0
|
|
|
|
|
0
|
$self->Color(@{$opts{'color'}}); |
|
0
|
|
|
|
|
0
|
|
417
|
|
|
|
|
|
|
} else { |
418
|
0
|
|
|
|
|
0
|
$self->Color([]); |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
# popup label (title) |
421
|
|
|
|
|
|
|
# have seen /T as (xFEFF UTF-16 chars) |
422
|
0
|
0
|
|
|
|
0
|
$self->{'T'} = PDFString($opts{'text'}, 'p') if exists $opts{'text'}; |
423
|
|
|
|
|
|
|
# opacity? |
424
|
0
|
0
|
|
|
|
0
|
if (defined $opts{'opacity'}) { |
425
|
0
|
|
|
|
|
0
|
$self->{'CA'} = PDFNum($opts{'opacity'}); |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
0
|
|
|
|
|
0
|
return $self; |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
=item $annotation->movie($file, $contentType, %opts) |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
Defines the annotation as a movie from C<$file> with |
434
|
|
|
|
|
|
|
content (MIME) type C<$contentType> and |
435
|
|
|
|
|
|
|
options %opts (rect, border, color, text: see descriptions below). |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
The C rectangle also serves as the area where the movie is played, so it |
438
|
|
|
|
|
|
|
should be of usable size and aspect ratio. It does not use a separate popup |
439
|
|
|
|
|
|
|
player. It is known to play .avi and .wav files -- others have not been tested. |
440
|
|
|
|
|
|
|
Using Adobe Reader, it will not play .mpg files (unsupported type). More work |
441
|
|
|
|
|
|
|
is probably needed on this annotation method. |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=cut |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
sub movie { |
446
|
0
|
|
|
0
|
1
|
0
|
my ($self, $file, $contentType, %opts) = @_; |
447
|
|
|
|
|
|
|
# copy dashed names over to preferred non-dashed names |
448
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-rect'} && !defined $opts{'rect'}) { $opts{'rect'} = delete($opts{'-rect'}); } |
|
0
|
|
|
|
|
0
|
|
449
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-color'} && !defined $opts{'color'}) { $opts{'color'} = delete($opts{'-color'}); } |
|
0
|
|
|
|
|
0
|
|
450
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-border'} && !defined $opts{'border'}) { $opts{'border'} = delete($opts{'-border'}); } |
|
0
|
|
|
|
|
0
|
|
451
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-text'} && !defined $opts{'text'}) { $opts{'text'} = delete($opts{'-text'}); } |
|
0
|
|
|
|
|
0
|
|
452
|
|
|
|
|
|
|
|
453
|
0
|
|
|
|
|
0
|
$self->{'Subtype'} = PDFName('Movie'); # subtype = movie (req) |
454
|
0
|
|
|
|
|
0
|
$self->{'A'} = PDFBool(1); # play using default activation parms |
455
|
0
|
|
|
|
|
0
|
$self->{'Movie'} = PDFDict(); |
456
|
|
|
|
|
|
|
#$self->{'Movie'}->{'S'} = PDFName($contentType); |
457
|
0
|
|
|
|
|
0
|
$self->{'Movie'}->{'F'} = PDFString($file, 'f'); |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# PDF::API2 2.034 changes don't seem to work |
460
|
|
|
|
|
|
|
# $self->{'Movie'}->{'F'} = PDFString($file, 'f'); line above removed |
461
|
|
|
|
|
|
|
#$self->{'Movie'}->{'F'} = PDFDict(); |
462
|
|
|
|
|
|
|
#$self->{' apipdf'}->new_obj($self->{'Movie'}->{'F'}); |
463
|
|
|
|
|
|
|
#my $f = $self->{'Movie'}->{'F'}; |
464
|
|
|
|
|
|
|
#$f->{'Type'} = PDFName('EmbeddedFile'); |
465
|
|
|
|
|
|
|
#$f->{'Subtype'} = PDFName($contentType); |
466
|
|
|
|
|
|
|
#$f->{' streamfile'} = $file; |
467
|
|
|
|
|
|
|
|
468
|
0
|
0
|
|
|
|
0
|
$self->rect(@{$opts{'rect'}}) if defined $opts{'rect'}; |
|
0
|
|
|
|
|
0
|
|
469
|
0
|
0
|
|
|
|
0
|
$self->border(@{$opts{'border'}}) if defined $opts{'border'}; |
|
0
|
|
|
|
|
0
|
|
470
|
0
|
0
|
|
|
|
0
|
$self->Color(@{$opts{'color'}}) if defined $opts{'color'}; |
|
0
|
|
|
|
|
0
|
|
471
|
|
|
|
|
|
|
# popup label (title) DOESN'T SEEM TO SHOW UP ANYWHERE |
472
|
|
|
|
|
|
|
# self->A->T and self->T also fail to display |
473
|
0
|
0
|
|
|
|
0
|
$self->{'Movie'}->{'T'} = PDFString($opts{'text'}, 'p') if exists $opts{'text'}; |
474
|
|
|
|
|
|
|
|
475
|
0
|
|
|
|
|
0
|
return $self; |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=item $annotation->file_attachment($file, %opts) |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
Defines the annotation as a file attachment with file $file and options %opts |
481
|
|
|
|
|
|
|
(rect, color: see descriptions below). Note that C applies to |
482
|
|
|
|
|
|
|
the icon fill color, not to a selectable area outline. The icon is resized |
483
|
|
|
|
|
|
|
(including aspect ratio changes) based on the selectable rectangle given by |
484
|
|
|
|
|
|
|
C, so watch your rectangle dimensions! |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
The file, along with its name, is I in the PDF document and may be |
487
|
|
|
|
|
|
|
extracted for viewing with the appropriate viewer. |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
This differs from the C method in that C looks for and launches |
490
|
|
|
|
|
|
|
a file I on the Reader's machine, while C embeds the |
491
|
|
|
|
|
|
|
file in the PDF, and makes it available on the Reader's machine for actions |
492
|
|
|
|
|
|
|
of the user's choosing. |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
B some Readers may only permit an "open" action, and may also restrict |
495
|
|
|
|
|
|
|
file types (extensions) that will be handled. This may be configurable with |
496
|
|
|
|
|
|
|
your Reader's security settings. |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
B the displayed file name (pop-up during mouse rollover of the target |
499
|
|
|
|
|
|
|
rectangle) is given with the I trimmed off (file name only). If you want |
500
|
|
|
|
|
|
|
the displayed name to exactly match the path that was passed to the call, |
501
|
|
|
|
|
|
|
including the path, give the C option. |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
Options: |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
=over |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=item icon => name_string |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
=item icon => reference |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
Specify the B to be used. The default is Reader-specific (usually |
512
|
|
|
|
|
|
|
C), and others may be |
513
|
|
|
|
|
|
|
defined by the Reader. C, C, and C are also supposed to |
514
|
|
|
|
|
|
|
be available on all PDF Readers. Note that the name I must exactly match. |
515
|
|
|
|
|
|
|
C is a custom invisible icon defined by PDF::Builder. |
516
|
|
|
|
|
|
|
The icon is stretched/squashed to fill the defined target rectangle, so take |
517
|
|
|
|
|
|
|
care when defining C dimensions. |
518
|
|
|
|
|
|
|
Any I dictionary entry will override the icon setting. |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
A I to an icon may be passed instead of a name. |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
=item opacity => I |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
Define the opacity (non-transparency, opaqueness) of the icon. This value |
525
|
|
|
|
|
|
|
ranges from 0.0 (transparent) to 1.0 (fully opaque), and applies to both |
526
|
|
|
|
|
|
|
the outline and the fill color. The default is 1.0. |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
=item notrimpath => 1 |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
If given, show the entire path and file name on mouse rollover, rather than |
531
|
|
|
|
|
|
|
just the file name. |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=item text => string |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
A text label for the popup (on mouseover) that contains the file name. |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=back |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
Note that while PDF permits different specifications (paths) to DOS/Windows, |
540
|
|
|
|
|
|
|
Mac, and Unix (including Linux) versions of a file, and different format copies |
541
|
|
|
|
|
|
|
to be embedded, at this time PDF::Builder only permits a single file (format of |
542
|
|
|
|
|
|
|
your choice) to be embedded. If there is user demand for multiple file formats |
543
|
|
|
|
|
|
|
to be referenced and/or embedded, we could look into providing this, I
|
544
|
|
|
|
|
|
|
separate OS version paths B be considered obsolescent!>. |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
=cut |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# TBD it is possible to specify different files for DOS, Mac, Unix |
549
|
|
|
|
|
|
|
# (see PDF 1.7 7.11.4.2). This might solve problem of different line |
550
|
|
|
|
|
|
|
# ends, at the cost of 3 copies of each file. |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
sub file_attachment { |
553
|
0
|
|
|
0
|
1
|
0
|
my ($self, $file, %opts) = @_; |
554
|
|
|
|
|
|
|
# copy dashed names over to preferred non-dashed names |
555
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-rect'} && !defined $opts{'rect'}) { $opts{'rect'} = delete($opts{'-rect'}); } |
|
0
|
|
|
|
|
0
|
|
556
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-color'} && !defined $opts{'color'}) { $opts{'color'} = delete($opts{'-color'}); } |
|
0
|
|
|
|
|
0
|
|
557
|
|
|
|
|
|
|
# if (defined $opts{'-border'} && !defined $opts{'border'}) { $opts{'border'} = delete($opts{'-border'}); } |
558
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-text'} && !defined $opts{'text'}) { $opts{'text'} = delete($opts{'-text'}); } |
|
0
|
|
|
|
|
0
|
|
559
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-opacity'} && !defined $opts{'opacity'}) { $opts{'opacity'} = delete($opts{'-opacity'}); } |
|
0
|
|
|
|
|
0
|
|
560
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-icon'} && !defined $opts{'icon'}) { $opts{'icon'} = delete($opts{'-icon'}); } |
|
0
|
|
|
|
|
0
|
|
561
|
0
|
0
|
0
|
|
|
0
|
if (defined $opts{'-notrimpath'} && !defined $opts{'notrimpath'}) { $opts{'notrimpath'} = delete($opts{'-notrimpath'}); } |
|
0
|
|
|
|
|
0
|
|
562
|
|
|
|
|
|
|
|
563
|
0
|
|
|
|
|
0
|
my $icon; # defaults to Reader's default (usually PushPin) |
564
|
0
|
0
|
|
|
|
0
|
$icon = $opts{'icon'} if exists $opts{'icon'}; |
565
|
|
|
|
|
|
|
|
566
|
0
|
0
|
|
|
|
0
|
$self->rect(@{$opts{'rect'}}) if defined $opts{'rect'}; |
|
0
|
|
|
|
|
0
|
|
567
|
|
|
|
|
|
|
# descriptive text on mouse rollover |
568
|
0
|
0
|
|
|
|
0
|
$self->{'T'} = PDFString($opts{'text'}, 'p') if exists $opts{'text'}; |
569
|
|
|
|
|
|
|
# icon opacity? |
570
|
0
|
0
|
|
|
|
0
|
if (defined $opts{'opacity'}) { |
571
|
0
|
|
|
|
|
0
|
$self->{'CA'} = PDFNum($opts{'opacity'}); |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
0
|
|
|
|
|
0
|
$self->{'Subtype'} = PDFName('FileAttachment'); |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
# 9 0 obj << |
577
|
|
|
|
|
|
|
# /Type /Annot |
578
|
|
|
|
|
|
|
# /Subtype /FileAttachment |
579
|
|
|
|
|
|
|
# /Name /PushPin |
580
|
|
|
|
|
|
|
# /C [ 1 1 0 ] |
581
|
|
|
|
|
|
|
# /Contents (test.txt) |
582
|
|
|
|
|
|
|
# /FS << |
583
|
|
|
|
|
|
|
# /Type /F |
584
|
|
|
|
|
|
|
# /EF << /F 10 0 R >> |
585
|
|
|
|
|
|
|
# /F (test.txt) |
586
|
|
|
|
|
|
|
# >> |
587
|
|
|
|
|
|
|
# /Rect [ 100 100 200 200 ] |
588
|
|
|
|
|
|
|
# /Border [ 0 0 1 ] |
589
|
|
|
|
|
|
|
# >> endobj |
590
|
|
|
|
|
|
|
# |
591
|
|
|
|
|
|
|
# 10 0 obj << |
592
|
|
|
|
|
|
|
# /Type /EmbeddedFile |
593
|
|
|
|
|
|
|
# /Length ... |
594
|
|
|
|
|
|
|
# >> stream |
595
|
|
|
|
|
|
|
# ... |
596
|
|
|
|
|
|
|
# endstream endobj |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
# text label on pop-up for mouse rollover |
599
|
0
|
|
|
|
|
0
|
my $cName = $file; |
600
|
|
|
|
|
|
|
# trim off any path, leaving just the file name. less confusing that way |
601
|
0
|
0
|
|
|
|
0
|
if (!defined $opts{'notrimpath'}) { |
602
|
0
|
0
|
|
|
|
0
|
if ($cName =~ m#([^/\\]+)$#) { $cName = $1; } |
|
0
|
|
|
|
|
0
|
|
603
|
|
|
|
|
|
|
} |
604
|
0
|
|
|
|
|
0
|
$self->{'Contents'} = PDFString($cName, 's'); |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
# Icon Name will be ignored if there is an AP. |
607
|
0
|
0
|
0
|
|
|
0
|
$self->{'Name'} = PDFName($icon) if $icon && !ref($icon); # icon name |
608
|
|
|
|
|
|
|
#$self->{'F'} = PDFNum(0b0); # flags default to 0 |
609
|
0
|
0
|
|
|
|
0
|
$self->Color(@{ $opts{'color'} }) if defined $opts{'color'}; |
|
0
|
|
|
|
|
0
|
|
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
# The File Specification. |
612
|
0
|
|
|
|
|
0
|
$self->{'FS'} = PDFDict(); |
613
|
0
|
|
|
|
|
0
|
$self->{'FS'}->{'F'} = PDFString($file, 'f'); |
614
|
0
|
|
|
|
|
0
|
$self->{'FS'}->{'Type'} = PDFName('Filespec'); |
615
|
0
|
|
|
|
|
0
|
$self->{'FS'}->{'EF'} = PDFDict($file); |
616
|
0
|
|
|
|
|
0
|
$self->{'FS'}->{'EF'}->{'F'} = PDFDict($file); |
617
|
0
|
|
|
|
|
0
|
$self->{' apipdf'}->new_obj($self->{'FS'}->{'EF'}->{'F'}); |
618
|
0
|
|
|
|
|
0
|
$self->{'FS'}->{'EF'}->{'F'}->{'Type'} = PDFName('EmbeddedFile'); |
619
|
0
|
|
|
|
|
0
|
$self->{'FS'}->{'EF'}->{'F'}->{' streamfile'} = $file; |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
# Set the icon appearance |
622
|
0
|
0
|
|
|
|
0
|
$self->icon_appearance($icon, %opts) if $icon; |
623
|
|
|
|
|
|
|
|
624
|
0
|
|
|
|
|
0
|
return $self; |
625
|
|
|
|
|
|
|
} |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
# TBD additional annotation types without icons |
628
|
|
|
|
|
|
|
# free text, line, square, circle, polygon (1.5), polyline (1.5), highlight, |
629
|
|
|
|
|
|
|
# underline, squiggly, strikeout, caret (1.5), ink, popup, sound, widget, |
630
|
|
|
|
|
|
|
# screen (1.5), printermark, trapnet, watermark (1.6), 3D (1.6), redact (1.7) |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
# TBD additional annotation types with icons |
633
|
|
|
|
|
|
|
# stamp |
634
|
|
|
|
|
|
|
# icons: Approved, Experimental, NotApproved, Asis, Expired, |
635
|
|
|
|
|
|
|
# NotForPublicRelease, Confidential, Final, Sold, Departmental, |
636
|
|
|
|
|
|
|
# ForComment, TopSecret, Draft (def.), ForPublicRelease |
637
|
|
|
|
|
|
|
# sound |
638
|
|
|
|
|
|
|
# icons: Speaker (def.), Mic |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
# =============== end of annotation types ======================== |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
=back |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
=head2 Internal routines and common options |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
=over |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
=item $annotation->rect($llx,$lly, $urx,$ury) |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
Sets the rectangle (active click area) of the annotation, given by 'rect' |
651
|
|
|
|
|
|
|
option. This is any pair of diagonally opposite corners of the rectangle. |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
The default clickable area is the icon itself. |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
Defining option. I.> |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
=over |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
=item rect => [LLx, LLy, URx, URy] |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
Set annotation rectangle at C<[LLx,LLy]> to C<[URx,URy]> (lower left and |
662
|
|
|
|
|
|
|
upper right coordinates). LL to UR is customary, but any diagonal is allowed. |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
=back |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=cut |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
sub rect { |
669
|
2
|
|
|
2
|
1
|
6
|
my ($self, @r) = @_; |
670
|
|
|
|
|
|
|
|
671
|
2
|
50
|
|
|
|
6
|
die "Insufficient parameters to annotation->rect() " unless scalar @r == 4; |
672
|
2
|
|
|
|
|
7
|
$self->{'Rect'} = PDFArray( map { PDFNum($_) } $r[0],$r[1],$r[2],$r[3]); |
|
8
|
|
|
|
|
17
|
|
673
|
2
|
|
|
|
|
5
|
return $self; |
674
|
|
|
|
|
|
|
} |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
=item $annotation->border(@b) |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
Sets the border-style of the annotation, if applicable, as given by the |
679
|
|
|
|
|
|
|
border option. There are three entries in the array: |
680
|
|
|
|
|
|
|
horizontal and vertical corner radii, and border width. |
681
|
|
|
|
|
|
|
An optional fourth entry (described below) may be used for a dashed or dotted |
682
|
|
|
|
|
|
|
line. |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
A border is used in annotations where text or some other material is put down, |
685
|
|
|
|
|
|
|
and a clickable rectangle is defined over it (rect). A border is not shown |
686
|
|
|
|
|
|
|
when an B is being used to mark the clickable area. |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
A I normally defaults to [0 0 1] (solid line of width 1, with |
689
|
|
|
|
|
|
|
sharp corners) if no border (C) is specified. Keeping compatibility |
690
|
|
|
|
|
|
|
with PDF::API2's longstanding practice, PDF::Builder defaults to no visible |
691
|
|
|
|
|
|
|
border C<[0 0 0]> (solid line of width 0, and thus invisible). |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
Defining option: |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
=over |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
=item border => [CRh, CRv, W] |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
=item border => [CRh, CRv, W, [on, off...]] |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
Note that the square brackets [ and ] are literally I, indicating a |
702
|
|
|
|
|
|
|
vector or array of values. They do B indicate optional values! |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
Set annotation B of horizontal and vertical corner radii C |
705
|
|
|
|
|
|
|
and C (value 0 for squared corners) and width C (value 0 for no border). |
706
|
|
|
|
|
|
|
The PDF::Builder default is no border (while a I typically defaults |
707
|
|
|
|
|
|
|
to no border ([0 0 0]), if no /Border entry is given). |
708
|
|
|
|
|
|
|
Optionally, a dash pattern array may be given (C length, C length, |
709
|
|
|
|
|
|
|
as one or more I). The default is a solid line. |
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
The border vector seems to ignore the first two settings (corner radii), but |
712
|
|
|
|
|
|
|
the line thickness works, on basic Readers. |
713
|
|
|
|
|
|
|
The corner radii I work on some other Readers. |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
=back |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
=cut |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
sub border { |
720
|
0
|
|
|
0
|
1
|
0
|
my ($self, @b) = @_; |
721
|
|
|
|
|
|
|
|
722
|
0
|
0
|
|
|
|
0
|
if (scalar @b == 3) { |
|
|
0
|
|
|
|
|
|
723
|
0
|
|
|
|
|
0
|
$self->{'Border'} = PDFArray( map { PDFNum($_) } $b[0],$b[1],$b[2]); |
|
0
|
|
|
|
|
0
|
|
724
|
|
|
|
|
|
|
} elsif (scalar @b == 4) { |
725
|
|
|
|
|
|
|
# b[3] is an anonymous array |
726
|
0
|
|
|
|
|
0
|
my @first = map { PDFNum($_) } $b[0], $b[1], $b[2]; |
|
0
|
|
|
|
|
0
|
|
727
|
0
|
|
|
|
|
0
|
$self->{'Border'} = PDFArray( @first, PDFArray( map { PDFNum($_) } @{$b[3]} )); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
728
|
|
|
|
|
|
|
} else { |
729
|
0
|
|
|
|
|
0
|
die "annotation->border() style requires 3 or 4 parameters "; |
730
|
|
|
|
|
|
|
} |
731
|
0
|
|
|
|
|
0
|
return $self; |
732
|
|
|
|
|
|
|
} |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
=item $annotation->content(@lines) |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
Sets the text-content of the C annotation. |
737
|
|
|
|
|
|
|
This is a text string or array of strings. |
738
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
=cut |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
sub content { |
742
|
2
|
|
|
2
|
1
|
6
|
my ($self, @lines) = @_; |
743
|
2
|
|
|
|
|
6
|
my $text = join("\n", @lines); |
744
|
|
|
|
|
|
|
|
745
|
2
|
|
|
|
|
6
|
$self->{'Contents'} = PDFString($text, 's'); |
746
|
2
|
|
|
|
|
4
|
return $self; |
747
|
|
|
|
|
|
|
} |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
# unused internal routine? TBD |
750
|
|
|
|
|
|
|
sub name { |
751
|
0
|
|
|
0
|
0
|
0
|
my ($self, $name) = @_; |
752
|
0
|
|
|
|
|
0
|
$self->{'Name'} = PDFName($name); |
753
|
0
|
|
|
|
|
0
|
return $self; |
754
|
|
|
|
|
|
|
} |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
=item $annotation->open($bool) |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
Display the C annotation either open or closed, if applicable. |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
Both are editable; the "open" form brings up the page with the entry area |
761
|
|
|
|
|
|
|
already open for editing, while "closed" has to be clicked on to edit it. |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
Defining option: |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
=over |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
=item open => boolean |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
If true (1), the annotation will be marked as initially "open". |
770
|
|
|
|
|
|
|
If false (0), or the option is not given, the annotation is initially "closed". |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
=back |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
=cut |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
sub open { ## no critic |
777
|
0
|
|
|
0
|
1
|
0
|
my ($self, $bool) = @_; |
778
|
0
|
0
|
|
|
|
0
|
$self->{'Open'} = PDFBool($bool? 1: 0); |
779
|
0
|
|
|
|
|
0
|
return $self; |
780
|
|
|
|
|
|
|
} |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
=item $annotation->dest($page, I) |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
For certain annotation types (C or C), the I |
785
|
|
|
|
|
|
|
specifies how the content of the page C<$page> is to be fit to the window, |
786
|
|
|
|
|
|
|
while preserving its aspect ratio. |
787
|
|
|
|
|
|
|
These fit settings are listed in L. |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
"xyz" is the B fit setting, with position (left and top) and zoom |
790
|
|
|
|
|
|
|
the same as the calling page's ([undef, undef, undef]). |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
=item $annotation->dest($name) |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
Connect the Annotation to a "Named Destination" defined elsewhere, including |
795
|
|
|
|
|
|
|
the optional desired I (default: xyz undef*3). |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
=cut |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
sub dest { |
800
|
2
|
|
|
2
|
1
|
4
|
my ($self, $page, %position) = @_; |
801
|
|
|
|
|
|
|
# copy dashed names over to preferred non-dashed names |
802
|
2
|
50
|
33
|
|
|
9
|
if (defined $position{'-fit'} && !defined $position{'fit'}) { $position{'fit'} = delete($position{'-fit'}); } |
|
0
|
|
|
|
|
0
|
|
803
|
2
|
50
|
33
|
|
|
7
|
if (defined $position{'-fith'} && !defined $position{'fith'}) { $position{'fith'} = delete($position{'-fith'}); } |
|
0
|
|
|
|
|
0
|
|
804
|
2
|
50
|
33
|
|
|
19
|
if (defined $position{'-fitb'} && !defined $position{'fitb'}) { $position{'fitb'} = delete($position{'-fitb'}); } |
|
0
|
|
|
|
|
0
|
|
805
|
2
|
50
|
33
|
|
|
6
|
if (defined $position{'-fitbh'} && !defined $position{'fitbh'}) { $position{'fitbh'} = delete($position{'-fitbh'}); } |
|
0
|
|
|
|
|
0
|
|
806
|
2
|
50
|
33
|
|
|
6
|
if (defined $position{'-fitv'} && !defined $position{'fitv'}) { $position{'fitv'} = delete($position{'-fitv'}); } |
|
0
|
|
|
|
|
0
|
|
807
|
2
|
50
|
33
|
|
|
12
|
if (defined $position{'-fitbv'} && !defined $position{'fitbv'}) { $position{'fitbv'} = delete($position{'-fitbv'}); } |
|
0
|
|
|
|
|
0
|
|
808
|
2
|
50
|
33
|
|
|
8
|
if (defined $position{'-fitr'} && !defined $position{'fitr'}) { $position{'fitr'} = delete($position{'-fitr'}); } |
|
0
|
|
|
|
|
0
|
|
809
|
2
|
50
|
33
|
|
|
7
|
if (defined $position{'-xyz'} && !defined $position{'xyz'}) { $position{'xyz'} = delete($position{'-xyz'}); } |
|
0
|
|
|
|
|
0
|
|
810
|
|
|
|
|
|
|
|
811
|
2
|
50
|
|
|
|
12
|
if (ref $page) { |
812
|
2
|
|
33
|
|
|
7
|
$self->{'A'} //= PDFDict(); |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
# old-fashioned 'fittype' => value |
815
|
2
|
50
|
|
|
|
13
|
if (defined $position{'fit'}) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
816
|
0
|
|
|
|
|
0
|
$self->{'A'}->{'D'} = PDFArray($page, PDFName('Fit')); |
817
|
|
|
|
|
|
|
} elsif (defined $position{'fith'}) { |
818
|
0
|
|
|
|
|
0
|
$self->{'A'}->{'D'} = PDFArray($page, PDFName('FitH'), PDFNum($position{'fith'})); |
819
|
|
|
|
|
|
|
} elsif (defined $position{'fitb'}) { |
820
|
0
|
|
|
|
|
0
|
$self->{'A'}->{'D'} = PDFArray($page, PDFName('FitB')); |
821
|
|
|
|
|
|
|
} elsif (defined $position{'fitbh'}) { |
822
|
0
|
|
|
|
|
0
|
$self->{'A'}->{'D'} = PDFArray($page, PDFName('FitBH'), PDFNum($position{'fitbh'})); |
823
|
|
|
|
|
|
|
} elsif (defined $position{'fitv'}) { |
824
|
0
|
|
|
|
|
0
|
$self->{'A'}->{'D'} = PDFArray($page, PDFName('FitV'), PDFNum($position{'fitv'})); |
825
|
|
|
|
|
|
|
} elsif (defined $position{'fitbv'}) { |
826
|
0
|
|
|
|
|
0
|
$self->{'A'}->{'D'} = PDFArray($page, PDFName('FitBV'), PDFNum($position{'fitbv'})); |
827
|
|
|
|
|
|
|
} elsif (defined $position{'fitr'}) { |
828
|
0
|
0
|
|
|
|
0
|
die "Insufficient parameters to fitr => []) " unless scalar @{$position{'fitr'}} == 4; |
|
0
|
|
|
|
|
0
|
|
829
|
0
|
|
|
|
|
0
|
$self->{'A'}->{'D'} = PDFArray($page, PDFName('FitR'), map {PDFNum($_)} @{$position{'fitr'}}); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
830
|
|
|
|
|
|
|
} elsif (defined $position{'xyz'}) { |
831
|
0
|
0
|
|
|
|
0
|
die "Insufficient parameters to xyz => []) " unless scalar @{$position{'xyz'}} == 3; |
|
0
|
|
|
|
|
0
|
|
832
|
0
|
0
|
|
|
|
0
|
$self->{'A'}->{'D'} = PDFArray($page, PDFName('XYZ'), map {defined $_ ? PDFNum($_) : PDFNull()} @{$position{'xyz'}}); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
833
|
|
|
|
|
|
|
} else { |
834
|
|
|
|
|
|
|
# no "fit" option found. use default. |
835
|
2
|
|
|
|
|
5
|
$position{'xyz'} = [undef,undef,undef]; |
836
|
2
|
50
|
|
|
|
6
|
$self->{'A'}->{'D'} = PDFArray($page, PDFName('XYZ'), map {defined $_ ? PDFNum($_) : PDFNull()} @{$position{'xyz'}}); |
|
6
|
|
|
|
|
32
|
|
|
2
|
|
|
|
|
5
|
|
837
|
|
|
|
|
|
|
} |
838
|
|
|
|
|
|
|
} else { |
839
|
0
|
|
|
|
|
0
|
$self->{'Dest'} = PDFString($page, 'n'); |
840
|
|
|
|
|
|
|
} |
841
|
|
|
|
|
|
|
|
842
|
2
|
|
|
|
|
6
|
return $self; |
843
|
|
|
|
|
|
|
} |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
=item $annotation->Color(@color) |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
Set the icon's fill color. The color is an array of 1, 3, or 4 numbers, each |
848
|
|
|
|
|
|
|
in the range 0.0 to 1.0. If 1 number is given, it is the grayscale value (0 = |
849
|
|
|
|
|
|
|
black to 1 = white). If 3 numbers are given, it is an RGB color value. If 4 |
850
|
|
|
|
|
|
|
numbers are given, it is a CMYK color value. Currently, named colors (strings) |
851
|
|
|
|
|
|
|
are not handled. |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
For link and url annotations, this is the color of the rectangle border |
854
|
|
|
|
|
|
|
(border given with a width of at least 1). |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
If an invalid array length or numeric value is given, a medium gray ( [0.5] ) |
857
|
|
|
|
|
|
|
value is used, without any message. If no color is given, the usual fill color |
858
|
|
|
|
|
|
|
is black. |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
Defining option: |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
Named colors are not supported at this time. |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
=over |
865
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
=item color => [ ] or not 1, 3, or 4 numbers 0.0-1.0 |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
A medium gray (0.5 value) will be used if an invalid color is given. |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
=item color => [ g ] |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
If I is between 0.0 (black) and 1.0 (white), the fill color will be gray. |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
=item color => [ r, g, b ] |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
If I (red), I (green), and I (blue) are all between 0.0 and 1.0, the |
877
|
|
|
|
|
|
|
fill color will be the defined RGB hue. [ 0, 0, 0 ] is black, [ 1, 1, 0 ] is |
878
|
|
|
|
|
|
|
yellow, and [ 1, 1, 1 ] is white. |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
=item color => [ c, m, y, k ] |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
If I (red), I (magenta), I (yellow), and I (black) are all between |
883
|
|
|
|
|
|
|
0.0 and 1.0, the fill color will be the defined CMYK hue. [ 0, 0, 0, 0 ] is |
884
|
|
|
|
|
|
|
white, [ 1, 0, 1, 0 ] is green, and [ 1, 1, 1, 1 ] is black. |
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
=back |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
=cut |
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
sub Color { |
891
|
0
|
|
|
0
|
1
|
|
my ($self, @color) = @_; |
892
|
|
|
|
|
|
|
|
893
|
0
|
0
|
0
|
|
|
|
if (scalar @color == 1 && |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
894
|
|
|
|
|
|
|
$color[0] >= 0 && $color[0] <= 1.0) { |
895
|
0
|
|
|
|
|
|
$self->{'C'} = PDFArray(map { PDFNum($_) } $color[0]); |
|
0
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
} elsif (scalar @color == 3 && |
897
|
|
|
|
|
|
|
$color[0] >= 0 && $color[0] <= 1.0 && |
898
|
|
|
|
|
|
|
$color[1] >= 0 && $color[1] <= 1.0 && |
899
|
|
|
|
|
|
|
$color[2] >= 0 && $color[2] <= 1.0) { |
900
|
0
|
|
|
|
|
|
$self->{'C'} = PDFArray(map { PDFNum($_) } $color[0], $color[1], $color[2]); |
|
0
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
} elsif (scalar @color == 4 && |
902
|
|
|
|
|
|
|
$color[0] >= 0 && $color[0] <= 1.0 && |
903
|
|
|
|
|
|
|
$color[1] >= 0 && $color[1] <= 1.0 && |
904
|
|
|
|
|
|
|
$color[2] >= 0 && $color[2] <= 1.0 && |
905
|
|
|
|
|
|
|
$color[3] >= 0 && $color[3] <= 1.0) { |
906
|
0
|
|
|
|
|
|
$self->{'C'} = PDFArray(map { PDFNum($_) } $color[0], $color[1], $color[2], $color[3]); |
|
0
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
} else { |
908
|
|
|
|
|
|
|
# invalid color entry. just set to medium gray without message |
909
|
0
|
|
|
|
|
|
$self->{'C'} = PDFArray(map { PDFNum($_) } 0.5 ); |
|
0
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
} |
911
|
|
|
|
|
|
|
|
912
|
0
|
|
|
|
|
|
return $self; |
913
|
|
|
|
|
|
|
} |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
=item text => string |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
Specify an optional B for annotation. This text or comment only |
918
|
|
|
|
|
|
|
shows up I in the pop-up containing the file or text. |
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
=cut |
921
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
sub icon_appearance { |
923
|
0
|
|
|
0
|
0
|
|
my ($self, $icon, %opts) = @_; |
924
|
|
|
|
|
|
|
# $icon is a string with name of icon (confirmed not empty) or a reference. |
925
|
|
|
|
|
|
|
# if a string (text), has already defined /Name. "None" and ref handle here. |
926
|
|
|
|
|
|
|
# options of interest: rect (to define size of icon) |
927
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
# copy dashed names over to preferred non-dashed names |
929
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-rect'} && !defined $opts{'rect'}) { $opts{'rect'} = delete($opts{'-rect'}); } |
|
0
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
# text also permits icon and custom icon, including None |
932
|
|
|
|
|
|
|
#return unless $self->{'Subtype'}->val() eq 'FileAttachment'; |
933
|
|
|
|
|
|
|
|
934
|
0
|
|
|
|
|
|
my @r; # perlcritic doesn't want 2 lines combined |
935
|
0
|
0
|
|
|
|
|
@r = @{$opts{'rect'}} if defined $opts{'rect'}; |
|
0
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
# number of parameters should be 4, checked above (rect method) |
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
# Handle custom icon type 'None' and icon reference. |
939
|
0
|
0
|
|
|
|
|
if ($icon eq 'None') { |
|
|
0
|
|
|
|
|
|
940
|
|
|
|
|
|
|
# It is not clear what viewers will do, so provide an |
941
|
|
|
|
|
|
|
# appearance dict with no graphics content. |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
# 9 0 obj << |
944
|
|
|
|
|
|
|
# ... |
945
|
|
|
|
|
|
|
# /AP << /D 11 0 R /N 11 0 R /R 11 0 R >> |
946
|
|
|
|
|
|
|
# ... |
947
|
|
|
|
|
|
|
# >> |
948
|
|
|
|
|
|
|
# 11 0 obj << |
949
|
|
|
|
|
|
|
# /BBox [ 0 0 100 100 ] |
950
|
|
|
|
|
|
|
# /FormType 1 |
951
|
|
|
|
|
|
|
# /Length 6 |
952
|
|
|
|
|
|
|
# /Matrix [ 1 0 0 1 0 0 ] |
953
|
|
|
|
|
|
|
# /Resources << |
954
|
|
|
|
|
|
|
# /ProcSet [ /PDF ] |
955
|
|
|
|
|
|
|
# >> |
956
|
|
|
|
|
|
|
# >> stream |
957
|
|
|
|
|
|
|
# 0 0 m |
958
|
|
|
|
|
|
|
# endstream endobj |
959
|
|
|
|
|
|
|
|
960
|
0
|
|
|
|
|
|
$self->{'AP'} = PDFDict(); |
961
|
0
|
|
|
|
|
|
my $d = PDFDict(); |
962
|
0
|
|
|
|
|
|
$self->{' apipdf'}->new_obj($d); |
963
|
0
|
|
|
|
|
|
$d->{'FormType'} = PDFNum(1); |
964
|
0
|
|
|
|
|
|
$d->{'Matrix'} = PDFArray(map { PDFNum($_) } 1, 0, 0, 1, 0, 0); |
|
0
|
|
|
|
|
|
|
965
|
0
|
|
|
|
|
|
$d->{'Resources'} = PDFDict(); |
966
|
0
|
|
|
|
|
|
$d->{'Resources'}->{'ProcSet'} = PDFArray( map { PDFName($_) } qw(PDF)); |
|
0
|
|
|
|
|
|
|
967
|
0
|
|
|
|
|
|
$d->{'BBox'} = PDFArray( map { PDFNum($_) } 0, 0, $r[2]-$r[0], $r[3]-$r[1] ); |
|
0
|
|
|
|
|
|
|
968
|
0
|
|
|
|
|
|
$d->{' stream'} = "0 0 m"; |
969
|
0
|
|
|
|
|
|
$self->{'AP'}->{'N'} = $d; # normal appearance |
970
|
|
|
|
|
|
|
# Should default to N, but be sure. |
971
|
0
|
|
|
|
|
|
$self->{'AP'}->{'R'} = $d; # Rollover |
972
|
0
|
|
|
|
|
|
$self->{'AP'}->{'D'} = $d; # Down |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
# Handle custom icon. |
975
|
|
|
|
|
|
|
} elsif (ref $icon) { |
976
|
|
|
|
|
|
|
# Provide an appearance dict with the image. |
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
# 9 0 obj << |
979
|
|
|
|
|
|
|
# ... |
980
|
|
|
|
|
|
|
# /AP << /D 11 0 R /N 11 0 R /R 11 0 R >> |
981
|
|
|
|
|
|
|
# ... |
982
|
|
|
|
|
|
|
# >> |
983
|
|
|
|
|
|
|
# 11 0 obj << |
984
|
|
|
|
|
|
|
# /BBox [ 0 0 1 1 ] |
985
|
|
|
|
|
|
|
# /FormType 1 |
986
|
|
|
|
|
|
|
# /Length 13 |
987
|
|
|
|
|
|
|
# /Matrix [ 1 0 0 1 0 0 ] |
988
|
|
|
|
|
|
|
# /Resources << |
989
|
|
|
|
|
|
|
# /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] |
990
|
|
|
|
|
|
|
# /XObject << /PxCBA 7 0 R >> |
991
|
|
|
|
|
|
|
# >> |
992
|
|
|
|
|
|
|
# >> stream |
993
|
|
|
|
|
|
|
# q /PxCBA Do Q |
994
|
|
|
|
|
|
|
# endstream endobj |
995
|
|
|
|
|
|
|
|
996
|
0
|
|
|
|
|
|
$self->{'AP'} = PDFDict(); |
997
|
0
|
|
|
|
|
|
my $d = PDFDict(); |
998
|
0
|
|
|
|
|
|
$self->{' apipdf'}->new_obj($d); |
999
|
0
|
|
|
|
|
|
$d->{'FormType'} = PDFNum(1); |
1000
|
0
|
|
|
|
|
|
$d->{'Matrix'} = PDFArray(map { PDFNum($_) } 1, 0, 0, 1, 0, 0); |
|
0
|
|
|
|
|
|
|
1001
|
0
|
|
|
|
|
|
$d->{'Resources'} = PDFDict(); |
1002
|
0
|
|
|
|
|
|
$d->{'Resources'}->{'ProcSet'} = PDFArray(map { PDFName($_) } qw(PDF Text ImageB ImageC ImageI)); |
|
0
|
|
|
|
|
|
|
1003
|
0
|
|
|
|
|
|
$d->{'Resources'}->{'XObject'} = PDFDict(); |
1004
|
0
|
|
|
|
|
|
my $im = $icon->{'Name'}->val(); |
1005
|
0
|
|
|
|
|
|
$d->{'Resources'}->{'XObject'}->{$im} = $icon; |
1006
|
|
|
|
|
|
|
# Note that the image is scaled to one unit in user space. |
1007
|
0
|
|
|
|
|
|
$d->{'BBox'} = PDFArray(map { PDFNum($_) } 0, 0, 1, 1); |
|
0
|
|
|
|
|
|
|
1008
|
0
|
|
|
|
|
|
$d->{' stream'} = "q /$im Do Q"; |
1009
|
0
|
|
|
|
|
|
$self->{'AP'}->{'N'} = $d; # normal appearance |
1010
|
|
|
|
|
|
|
|
1011
|
0
|
|
|
|
|
|
if (0) { |
1012
|
|
|
|
|
|
|
# Testing... Provide an alternative for R and D. |
1013
|
|
|
|
|
|
|
# Works only with Adobe Reader. |
1014
|
|
|
|
|
|
|
$d = PDFDict(); |
1015
|
|
|
|
|
|
|
$self->{' apipdf'}->new_obj($d); |
1016
|
|
|
|
|
|
|
$d->{'Type'} = PDFName('XObject'); |
1017
|
|
|
|
|
|
|
$d->{'Subtype'} = PDFName('Form'); |
1018
|
|
|
|
|
|
|
$d->{'FormType'} = PDFNum(1); |
1019
|
|
|
|
|
|
|
$d->{'Matrix'} = PDFArray(map { PDFNum($_) } 1, 0, 0, 1, 0, 0); |
1020
|
|
|
|
|
|
|
$d->{'Resources'} = PDFDict(); |
1021
|
|
|
|
|
|
|
$d->{'Resources'}->{'ProcSet'} = PDFArray(map { PDFName($_) } qw(PDF)); |
1022
|
|
|
|
|
|
|
$d->{'BBox'} = PDFArray(map { PDFNum($_) } 0, 0, $r[2]-$r[0], $r[3]-$r[1]); |
1023
|
|
|
|
|
|
|
$d->{' stream'} = |
1024
|
|
|
|
|
|
|
join( " ", |
1025
|
|
|
|
|
|
|
# black outline |
1026
|
|
|
|
|
|
|
0, 0, 'm', |
1027
|
|
|
|
|
|
|
0, $r[2]-$r[0], 'l', |
1028
|
|
|
|
|
|
|
$r[2]-$r[0], $r[3]-$r[1], 'l', |
1029
|
|
|
|
|
|
|
$r[2]-$r[0], 0, 'l', |
1030
|
|
|
|
|
|
|
's', |
1031
|
|
|
|
|
|
|
); |
1032
|
|
|
|
|
|
|
} |
1033
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
# Should default to N, but be sure. |
1035
|
0
|
|
|
|
|
|
$self->{'AP'}->{'R'} = $d; # Rollover |
1036
|
0
|
|
|
|
|
|
$self->{'AP'}->{'D'} = $d; # Down |
1037
|
|
|
|
|
|
|
} |
1038
|
|
|
|
|
|
|
|
1039
|
0
|
|
|
|
|
|
return $self; |
1040
|
|
|
|
|
|
|
} |
1041
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
=back |
1043
|
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
=cut |
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
1; |