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