package PDF::API2::Annotation; use base 'PDF::API2::Basic::PDF::Dict'; use strict; use warnings; our $VERSION = '2.043'; # VERSION use Carp; use PDF::API2::Basic::PDF::Utils; =head1 NAME PDF::API2::Annotation - Add annotations to a PDF =head1 SYNOPSIS my $pdf = PDF::API2->new(); my $font = $pdf->font('Helvetica'); my $page1 = $pdf->page(); my $page2 = $pdf->page(); my $content = $page1->text(); my $message = 'Go to Page 2'; my $size = 18; $content->distance(1 * 72, 9 * 72); $content->font($font, $size); $content->text($message); my $annotation = $page1->annotation(); my $width = $content->text_width($message); $annotation->rect(1 * 72, 9 * 72, 1 * 72 + $width, 9 * 72 + $size); $annotation->link($page2); $pdf->save('sample.pdf'); =head1 METHODS =cut sub new { my $class = shift(); my $self = $class->SUPER::new(); $self->{'Type'} = PDFName('Annot'); $self->{'Border'} = PDFArray(PDFNum(0), PDFNum(0), PDFNum(0)); return $self; } =head2 Annotation Types =head3 link $annotation = $annotation->link($destination, $location, @args); Link the annotation to another page in this PDF. C<$location> and C<@args> are optional and set which part of the page should be displayed, as defined in L. C<$destination> can be either a L object or the name of a named destination defined elsewhere. =cut sub link { my $self = shift(); my $destination = shift(); my $location; my @args; # Deprecated options my %options; if ($_[0] and $_[0] =~ /^-/) { %options = @_; } else { $location = shift(); @args = @_; } $self->{'Subtype'} = PDFName('Link'); unless (ref($destination)) { $self->{'Dest'} = PDFStr($destination); return $self; } $self->{'A'} = PDFDict(); $self->{'A'}->{'S'} = PDFName('GoTo'); unless (%options) { $self->{'A'}->{'D'} = _destination($destination, $location, @args); } else { # Deprecated $self->dest($destination, %options); $self->rect(@{$options{'-rect'}}) if defined $options{'-rect'}; $self->border(@{$options{'-border'}}) if defined $options{'-border'}; } return $self; } sub _destination { require PDF::API2::NamedDestination; return PDF::API2::NamedDestination::_destination(@_); } =head3 url $annotation = $annotation->uri($uri); Launch C<$uri> -- typically a web page -- when the annotation is selected. =cut # Deprecated (renamed) sub url { return uri(@_) } sub uri { my ($self, $uri, %options) = @_; $self->{'Subtype'} = PDFName('Link'); $self->{'A'} = PDFDict(); $self->{'A'}->{'S'} = PDFName('URI'); $self->{'A'}->{'URI'} = PDFStr($uri); # Deprecated $self->rect(@{$options{'-rect'}}) if defined $options{'-rect'}; $self->border(@{$options{'-border'}}) if defined $options{'-border'}; return $self; } =head3 file $annotation = $annotation->launch($file); Open C<$file> when the annotation is selected. =cut sub file { return launch(@_) } sub launch { my ($self, $file, %options) = @_; $self->{'Subtype'} = PDFName('Link'); $self->{'A'} = PDFDict(); $self->{'A'}->{'S'} = PDFName('Launch'); $self->{'A'}->{'F'} = PDFStr($file); # Deprecated $self->rect(@{$options{'-rect'}}) if defined $options{'-rect'}; $self->border(@{$options{'-border'}}) if defined $options{'-border'}; return $self; } =head3 pdf $annotation = $annotation->pdf($file, $page_number, $location, @args); Open the PDF file located at C<$file> to the specified page number. C<$location> and C<@args> are optional and set which part of the page should be displayed, as defined in L. =cut # Deprecated sub pdfile { return pdf_file(@_) } sub pdf_file { return pdf(@_) } sub pdf { my $self = shift(); my $file = shift(); my $page_number = shift(); my $location; my @args; # Deprecated options my %options; if ($_[0] and $_[0] =~ /^-/) { %options = @_; } else { $location = shift(); @args = @_; } $self->{'Subtype'} = PDFName('Link'); $self->{'A'} = PDFDict(); $self->{'A'}->{'S'} = PDFName('GoToR'); $self->{'A'}->{'F'} = PDFStr($file); unless (%options) { my $destination = PDFNum($page_number); $self->{'A'}->{'D'} = _destination($destination, $location, @args); } else { # Deprecated $self->dest(PDFNum($page_number), %options); $self->rect(@{$options{'-rect'}}) if defined $options{'-rect'}; $self->border(@{$options{'-border'}}) if defined $options{'-border'}; } return $self; } =head3 text $annotation = $annotation->text($text); Define the annotation as a text note with the specified content. =cut sub text { my ($self, $text, %options) = @_; $self->{'Subtype'} = PDFName('Text'); $self->content($text); # Deprecated $self->rect(@{$options{'-rect'}}) if defined $options{'-rect'}; $self->open($options{'-open'}) if defined $options{'-open'}; return $self; } =head3 movie $annotation = $annotation->movie($filename, $content_type); Embed and link to the movie located at $filename with the specified MIME type. =cut sub movie { my ($self, $file, $content_type, %options) = @_; $self->{'Subtype'} = PDFName('Movie'); $self->{'A'} = PDFBool(1); $self->{'Movie'} = PDFDict(); $self->{'Movie'}->{'F'} = PDFDict(); $self->{' apipdf'}->new_obj($self->{'Movie'}->{'F'}); my $f = $self->{'Movie'}->{'F'}; $f->{'Type'} = PDFName('EmbeddedFile'); $f->{'Subtype'} = PDFName($content_type); $f->{' streamfile'} = $file; # Deprecated $self->rect(@{$options{'-rect'}}) if defined $options{'-rect'}; return $self; } =head2 Common Annotation Attributes =head3 rect $annotation = $annotation->rect($llx, $lly, $urx, $ury); Define the rectangle around the annotation. =cut sub rect { my ($self, @coordinates) = @_; unless (scalar @coordinates == 4) { die "Incorrect number of parameters (expected four) for rectangle"; } $self->{'Rect'} = PDFArray(map { PDFNum($_) } @coordinates); return $self; } =head3 border $annotation = $annotation->border($h_radius, $v_radius, $width); Define the border style. Defaults to 0, 0, 0 (no border). =cut sub border { my ($self, @attributes) = @_; unless (scalar @attributes == 3) { croak "Incorrect number of parameters (expected three) for border"; } $self->{'Border'} = PDFArray(map { PDFNum($_) } @attributes); return $self; } =head3 content $annotation = $annotation->content(@lines); Define the text content of the annotation, if applicable. =cut sub content { my ($self, @lines) = @_; my $text = join("\n", @lines); $self->{'Contents'} = PDFStr($text); return $self; } sub name { my ($self, $name) = @_; $self->{'Name'} = PDFName($name); return $self; } =head3 open $annotation = $annotation->open($boolean); Set the annotation to initially be either open or closed. Only relevant for text annotations. =cut sub open { my ($self, $value) = @_; $self->{'Open'} = PDFBool($value ? 1 : 0); return $self; } sub dest { my ($self, $page, %options) = @_; unless (ref($page)) { $self->{'Dest'} = PDFStr($page); return $self; } $self->{'A'} //= PDFDict(); $options{'-xyz'} = [undef, undef, undef] unless keys %options; if (defined $options{'-fit'}) { $self->{'A'}->{'D'} = _destination($page, 'fit'); } elsif (defined $options{'-fith'}) { $self->{'A'}->{'D'} = _destination($page, 'fith', $options{'-fith'}); } elsif (defined $options{'-fitb'}) { $self->{'A'}->{'D'} = _destination($page, 'fitb'); } elsif (defined $options{'-fitbh'}) { $self->{'A'}->{'D'} = _destination($page, 'fitbh', $options{'-fitbh'}); } elsif (defined $options{'-fitv'}) { $self->{'A'}->{'D'} = _destination($page, 'fitv', $options{'-fitv'}); } elsif (defined $options{'-fitbv'}) { $self->{'A'}->{'D'} = _destination($page, 'fitbv', $options{'-fitbv'}); } elsif (defined $options{'-fitr'}) { $self->{'A'}->{'D'} = _destination($page, 'fitr', @{$options{'-fitr'}}); } elsif (defined $options{'-xyz'}) { $self->{'A'}->{'D'} = _destination($page, 'xyz', @{$options{'-xyz'}}); } return $self; } 1;