File Coverage

blib/lib/PDF/API2/Tweaks.pm
Criterion Covered Total %
statement 12 130 9.2
branch 0 58 0.0
condition 0 23 0.0
subroutine 4 18 22.2
pod 1 12 8.3
total 17 241 7.0


line stmt bran cond sub pod time code
1             #! perl
2              
3 1     1   17254 use strict;
  1         2  
  1         36  
4 1     1   4 use warnings;
  1         2  
  1         63  
5              
6 1     1   658 use PDF::API2;
  1         185477  
  1         110  
7              
8             package PDF::API2::Tweaks;
9              
10             =head1 NAME
11              
12             PDF::API2::Tweaks - Assorted handy additions to PDF::API2.
13              
14             =head1 SYNOPSIS
15              
16             PDF::API2::Tweaks provides a number of extensions to PDF::API2.
17              
18             Most of the extensions deal with producing PDF overlays, to fill in
19             forms. For example,
20              
21             # Open an existing PDF file
22             my $pdf = PDF::API2->open($form);
23              
24             # Retrieve an existing page
25             my $page = $pdf->openpage(1);
26              
27             # Add a built-in font to the PDF
28             my $font = $pdf->corefont('Helvetica');
29              
30             # Setup text context.
31             my $text = $page->text();
32             $text->font($font, 10);
33             $text->fillcolor('#000000');
34             $text->strokecolor('#000000');
35              
36             # So far, this is all basic PDF::API2.
37              
38             # The following Tweaks extension will produce a series of lines,
39             # the first one starting at position 100,714 and subsequent lines
40             # spaced 16 apart:
41              
42             $text->textlist( 100, 714, 16, <<'EOD' );
43             Now is the time
44             for all good man
45             to start using Perl
46             EOD
47              
48             # Save to a file.
49             $pdf->saveas("perl.pdf");
50              
51             =cut
52              
53             our $VERSION = 0.09;
54              
55             =head1 TEXT FUNCTIONS
56              
57             The following functions operate on PDF::API2::Content::Text objects.
58             In general, these are obtained by a call to the C method on the
59             page object.
60              
61             =cut
62              
63             package PDF::API2::Content::Text;
64              
65 1     1   7 use Carp;
  1         1  
  1         1211  
66              
67             sub _isnum {
68 0     0     $_[0] =~ /^[-+]?\d+(.\d+)?$/;
69             }
70              
71             my %rotation_for; # LEAK!!!
72              
73             sub translate { # internal
74 0     0 1   my ( $self, $x, $y ) = @_;
75 0 0         if ( $rotation_for{$self} ) {
76 0 0         ($x, $y) = ($y, $x) if $rotation_for{$self} == 90;
77 0           $self->transform( -translate => [ $x, $y ],
78             -rotate => $rotation_for{$self},
79             );
80 0           return;
81             }
82              
83 0           $self->SUPER::translate( $x, $y );
84             }
85              
86             sub set_rotation { # internal
87 0     0 0   my ( $self, $rotation ) = @_;
88 0 0         $rotation ? $rotation_for{$self} = $rotation : delete($rotation_for{$self});
89             }
90              
91             =head2 $text->textlist( X, Y, D, items )
92              
93             Writes a list of items starting at the given coordinates and
94             decrementing Y with D for each item. Note that items may contain
95             newlines that will be dwimmed.
96              
97             Returns the coordinates of the last item written; in scalar context
98             the Y coordinate only.
99              
100             =cut
101              
102             sub textlist {
103 0     0 0   my ( $self, $x, $y, $d, @list ) = @_;
104 0 0 0       croak("textlist: coordinates must be numeric, not ($x,$y)")
105             unless _isnum($x) && _isnum($y);
106 0 0         croak("textlist: line spacing must be numeric, not \"$d\"")
107             unless _isnum($d);
108              
109 0           foreach ( @list ) {
110 0           foreach ( split /\n/ ) {
111 0           $self->translate( $x, $y );
112 0           $self->text($_);
113 0           $y -= $d;
114             }
115             }
116 0 0         wantarray ? ( $x, $y + $d ) : $y + $d;
117             }
118              
119             =head2 $text->textline( X, Y, line )
120              
121             Writes a line of text at the given coordinates.
122              
123             Returns the coordinates; in scalar context the Y coordinate only.
124              
125             =cut
126              
127             sub textline {
128 0     0 0   my ( $self, $x, $y, $line ) = @_;
129 0 0 0       croak("textline: coordinates must be numeric, not ($x,$y)")
130             unless _isnum($x) && _isnum($y);
131              
132 0           $self->translate( $x, $y );
133 0           $self->text($line);
134 0 0         wantarray ? ( $x, $y ) : $y;
135             }
136              
137             =head2 $text->textrline( X, Y, line )
138              
139             Writes a line of text at the given coordinates, right aligned.
140              
141             Returns the coordinates; in scalar context the Y coordinate only.
142              
143             =cut
144              
145             sub textrline {
146 0     0 0   my ( $self, $x, $y, $line ) = @_;
147 0 0 0       croak("textrline: coordinates must be numeric, not ($x,$y)")
148             unless _isnum($x) && _isnum($y);
149              
150 0           $self->translate( $x, $y );
151 0           $self->text_right($line);
152 0 0         wantarray ? ( $x, $y ) : $y;
153             }
154              
155             =head2 $text->textcline( X, Y, line )
156              
157             Writes a line of text at the given coordinates, centered.
158              
159             Returns the coordinates; in scalar context the Y coordinate only.
160              
161             =cut
162              
163             sub textcline {
164 0     0 0   my ( $self, $x, $y, $line ) = @_;
165 0 0 0       croak("textcline: coordinates must be numeric, not ($x,$y)")
166             unless _isnum($x) && _isnum($y);
167              
168 0           $self->translate( $x, $y );
169 0           $self->text_center($line);
170 0 0         wantarray ? ( $x, $y ) : $y;
171             }
172              
173             =head2 $text->texthlist( X, Y, item, [ disp, item, ... ] )
174              
175             Writes a series of items at the given coordinates, each subsequent
176             item is horizontally offsetted by the displacement that precedes it
177             in the list.
178              
179             Returns the coordinates of the last item written; in scalar context
180             the X coordinate only.
181              
182             =cut
183              
184             sub texthlist {
185 0     0 0   my ( $self, $x, $y, @list ) = @_;
186 0 0 0       croak("texthlist: coordinates must be numeric, not ($x,$y)")
187             unless _isnum($x) && _isnum($y);
188              
189 0           $self->translate( $x, $y );
190 0           $self->text( shift(@list) );
191 0           while ( @list ) {
192 0           my $d = shift(@list);
193 0 0         croak("texthlist: offset must be a number, not \"$d\"")
194             unless _isnum($d);
195 0           $x += $d;
196 0 0         last unless @list;
197 0           $self->translate( $x, $y );
198 0           $self->text( shift(@list) );
199             }
200 0 0         wantarray ? ( $x, $y ) : $x;
201             }
202              
203             =head2 $text->textvlist( X, Y, item, [ disp, item, ... ] )
204              
205             Writes a series of items at the given coordinates, each subsequent
206             item is vertically offsetted by the displacement that precedes it
207             in the list.
208              
209             Returns the coordinates of the last item written; in scalar context
210             the Y coordinate only.
211              
212             =cut
213              
214             sub textvlist {
215 0     0 0   my ( $self, $x, $y, @list ) = @_;
216 0 0 0       croak("textvlist: coordinates must be numeric, not ($x,$y)")
217             unless _isnum($x) && _isnum($y);
218              
219 0           $self->translate( $x, $y );
220 0           $self->text( shift(@list) );
221 0           while ( @list ) {
222 0           my $d = shift(@list);
223 0 0         croak("textvlist: offset must be a number, not \"$d\"")
224             unless _isnum($d);
225 0           $y -= $d;
226 0 0         last unless @list;
227 0           $self->translate( $x, $y );
228 0           $self->text( shift(@list) );
229             }
230 0 0         wantarray ? ( $x, $y ) : $x;
231             }
232              
233             =head2 $text->textspread( X, Y, disp, item )
234              
235             Writes a text at the given coordinates, each individual letter
236             is horizontally offsetted by the displacement.
237              
238             Returns the coordinates of the last item written; in scalar context
239             the X coordinate only.
240              
241             =cut
242              
243             sub textspread {
244 0     0 0   my ( $self, $x, $y, $d, $line ) = @_;
245 0 0 0       croak("textspread: coordinates must be numeric, not ($x,$y)")
246             unless _isnum($x) && _isnum($y);
247 0 0         croak("textspread: spread must be numeric, not \"$d\"")
248             unless _isnum($d);
249              
250 0           for ( split( //, $line ) ) {
251 0           $self->translate($x, $y);
252 0           $self->text($_);
253 0           $x += $d;
254             }
255 0           $x -= $d;
256 0 0         wantarray ? ( $x, $y ) : $x;
257             }
258              
259             =head2 $text->textpara( X, Y, W, disp, indent, text )
260              
261             Writes a text in an rectangular area starting at X,Y and W width.
262             Lines are broken at whitespace.
263              
264             Returns the coordinates of the last item written; in scalar context
265             the Y coordinate only.
266              
267             =cut
268              
269             sub textpara {
270 0     0 0   my ( $self, $x, $y, $w, $d, $indent, $text ) = @_;
271              
272 0           my $t = '';
273 0           my $xx = $x;
274 0           $x += $indent;
275 0           my $l = $indent;
276              
277             # Get rid of trailing spaces.
278 0           $text =~ s/\s+$//;
279              
280 0           my @text = split( /\s+/, $text );
281 0           while ( @text ) {
282 0           my $word = shift(@text);
283 0 0         if ( ( $l += $self->advancewidth(" $word")) > $w ) {
284 0           $self->textline( $x, $y, $t );
285 0           $y -= $d;
286 0           $t = $word;
287 0           $x = $xx;
288 0           $l = $self->advancewidth($word)
289             }
290             else {
291 0 0         $t .= " " if length($t);
292 0           $t .= $word;
293             }
294             }
295              
296 0           $self->textline( $x, $y, $t);
297 0 0         wantarray ? ( $xx, $y ) : $y;
298             }
299              
300             =head2 $text->textparas( X, Y, W, disp, indent, text )
301              
302             Writes a text in an rectangular area starting at X,Y and W width.
303             Lines are broken at whitespace.
304             A newline indicates a new paragraph start.
305              
306             Returns the coordinates of the last item written; in scalar context
307             the Y coordinate only.
308              
309             =cut
310              
311             sub textparas {
312 0     0 0   my ( $self, $x, $y, $w, $d, $indent, $text ) = @_;
313              
314 0           $text =~ s/\r\n/\n/g;
315 0           my @text = split( /\n/, $text );
316 0           foreach ( @text ) {
317 0           $y = $self->textpara( $x, $y, $w, $d, $indent, $_ );
318 0           $y -= $d;
319             }
320 0 0         wantarray ? ( $x, $y + $d ) : $y;
321             }
322              
323             package PDF::API2::Page;
324              
325             =head1 PAGE FUNCTIONS
326              
327             The following functions operate on PDF::API2::Page objects.
328             In general, these are obtained by a call to the C method on the
329             PDF document object.
330              
331             =cut
332              
333             =head2 $page->grid( [ spacing ] )
334              
335             Draws a grid of coordinates on the page.
336             Lines a black for every 100, blue otherwise.
337              
338             =cut
339              
340             sub grid {
341 0     0 0   my ( $self, $spacing ) = @_;
342              
343 0           my $g = $self->gfx;
344 0           my $t = $self->text;
345              
346 0           $g->fillcolor('#000000');
347 0           $g->strokecolor('#000000');
348             # $g->font( ... );
349             my $colour = sub {
350 0 0   0     if ( $_[0] % 100 == 0 ) {
351 0           $g->linewidth(0.25);
352 0           $g->strokecolor('#808080');
353             }
354             else {
355 0           $g->linewidth(0.25);
356 0           $g->strokecolor('#aaaaff');
357             }
358 0           };
359              
360 0           $g->save;
361 0   0       $spacing ||= 100;
362 0 0         $spacing = 20 if $spacing == 1; # for convenience
363              
364 0           my ( $xmax, $ymax ) = ( 600, 900 );
365 0           for ( my $x = $spacing; $x < $xmax; $x += $spacing ) {
366 0           $colour->($x);
367 0           $g->move($x, 0);
368 0           $g->line($x, $ymax);
369 0           $g->stroke;
370             }
371 0           for ( my $y = $spacing; $y < $ymax; $y += $spacing ) {
372 0           $colour->($y);
373 0           $g->move(0, $y);
374 0           $g->line($xmax, $y);
375 0           $g->stroke;
376             # $g->move(5, $y+5);
377             # $g->text("$y");
378             }
379 0           $g->restore;
380             }
381              
382             =head1 BUGS
383              
384             There's a small memory leak for every text object that is used. For
385             normal use this can be ignored since you'll probably need just of
386             couple of text objects.
387              
388             =cut
389              
390             1;