File Coverage

blib/lib/PDF/API2/Tweaks.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             #! perl
2              
3 1     1   14429 use strict;
  1         2  
  1         35  
4 1     1   4 use warnings;
  1         1  
  1         24  
5              
6 1     1   189 use PDF::API2;
  0            
  0            
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.08;
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             use Carp;
66              
67             sub _isnum {
68             $_[0] =~ /^[-+]?\d+(.\d+)?$/;
69             }
70              
71             my %rotation_for; # LEAK!!!
72              
73             sub translate { # internal
74             my ( $self, $x, $y ) = @_;
75             if ( $rotation_for{$self} ) {
76             ($x, $y) = ($y, $x) if $rotation_for{$self} == 90;
77             $self->transform( -translate => [ $x, $y ],
78             -rotate => $rotation_for{$self},
79             );
80             return;
81             }
82              
83             $self->SUPER::translate( $x, $y );
84             }
85              
86             sub set_rotation { # internal
87             my ( $self, $rotation ) = @_;
88             $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             my ( $self, $x, $y, $d, @list ) = @_;
104             croak("textlist: coordinates must be numeric, not ($x,$y)")
105             unless _isnum($x) && _isnum($y);
106             croak("textlist: line spacing must be numeric, not \"$d\"")
107             unless _isnum($d);
108              
109             foreach ( @list ) {
110             foreach ( split /\n/ ) {
111             $self->translate( $x, $y );
112             $self->text($_);
113             $y -= $d;
114             }
115             }
116             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             my ( $self, $x, $y, $line ) = @_;
129             croak("textline: coordinates must be numeric, not ($x,$y)")
130             unless _isnum($x) && _isnum($y);
131              
132             $self->translate( $x, $y );
133             $self->text($line);
134             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             my ( $self, $x, $y, $line ) = @_;
147             croak("textrline: coordinates must be numeric, not ($x,$y)")
148             unless _isnum($x) && _isnum($y);
149              
150             $self->translate( $x, $y );
151             $self->text_right($line);
152             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             my ( $self, $x, $y, $line ) = @_;
165             croak("textcline: coordinates must be numeric, not ($x,$y)")
166             unless _isnum($x) && _isnum($y);
167              
168             $self->translate( $x, $y );
169             $self->text_center($line);
170             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             my ( $self, $x, $y, @list ) = @_;
186             croak("texthlist: coordinates must be numeric, not ($x,$y)")
187             unless _isnum($x) && _isnum($y);
188              
189             $self->translate( $x, $y );
190             $self->text( shift(@list) );
191             while ( @list ) {
192             my $d = shift(@list);
193             croak("texthlist: offset must be a number, not \"$d\"")
194             unless _isnum($d);
195             $x += $d;
196             last unless @list;
197             $self->translate( $x, $y );
198             $self->text( shift(@list) );
199             }
200             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             my ( $self, $x, $y, @list ) = @_;
216             croak("textvlist: coordinates must be numeric, not ($x,$y)")
217             unless _isnum($x) && _isnum($y);
218              
219             $self->translate( $x, $y );
220             $self->text( shift(@list) );
221             while ( @list ) {
222             my $d = shift(@list);
223             croak("textvlist: offset must be a number, not \"$d\"")
224             unless _isnum($d);
225             $y -= $d;
226             last unless @list;
227             $self->translate( $x, $y );
228             $self->text( shift(@list) );
229             }
230             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             my ( $self, $x, $y, $d, $line ) = @_;
245             croak("textspread: coordinates must be numeric, not ($x,$y)")
246             unless _isnum($x) && _isnum($y);
247             croak("textspread: spread must be numeric, not \"$d\"")
248             unless _isnum($d);
249              
250             for ( split( //, $line ) ) {
251             $self->translate($x, $y);
252             $self->text($_);
253             $x += $d;
254             }
255             $x -= $d;
256             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             my ( $self, $x, $y, $w, $d, $indent, $text ) = @_;
271              
272             my $t = '';
273             my $xx = $x;
274             $x += $indent;
275             my $l = $indent;
276              
277             # Get rid of trailing spaces.
278             $text =~ s/\s+$//;
279              
280             my @text = split( /\s+/, $text );
281             while ( @text ) {
282             my $word = shift(@text);
283             if ( ( $l += $self->advancewidth(" $word")) > $w ) {
284             $self->textline( $x, $y, $t );
285             $y -= $d;
286             $t = $word;
287             $x = $xx;
288             $l = $self->advancewidth($word)
289             }
290             else {
291             $t .= " " if length($t);
292             $t .= $word;
293             }
294             }
295              
296             $self->textline( $x, $y, $t);
297             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             my ( $self, $x, $y, $w, $d, $indent, $text ) = @_;
313              
314             $text =~ s/\r\n/\n/g;
315             my @text = split( /\n/, $text );
316             foreach ( @text ) {
317             $y = $self->textpara( $x, $y, $w, $d, $indent, $_ );
318             $y -= $d;
319             }
320             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             my ( $self, $spacing ) = @_;
342              
343             my $g = $self->gfx;
344             my $t = $self->text;
345              
346             $g->fillcolor('#000000');
347             $g->strokecolor('#000000');
348             # $g->font( ... );
349             my $colour = sub {
350             if ( $_[0] % 100 == 0 ) {
351             $g->linewidth(0.25);
352             $g->strokecolor('#808080');
353             }
354             else {
355             $g->linewidth(0.25);
356             $g->strokecolor('#aaaaff');
357             }
358             };
359              
360             $g->save;
361             $spacing ||= 100;
362             $spacing = 20 if $spacing == 1; # for convenience
363              
364             my ( $xmax, $ymax ) = ( 600, 900 );
365             for ( my $x = $spacing; $x < $xmax; $x += $spacing ) {
366             $colour->($x);
367             $g->move($x, 0);
368             $g->line($x, $ymax);
369             $g->stroke;
370             }
371             for ( my $y = $spacing; $y < $ymax; $y += $spacing ) {
372             $colour->($y);
373             $g->move(0, $y);
374             $g->line($xmax, $y);
375             $g->stroke;
376             # $g->move(5, $y+5);
377             # $g->text("$y");
378             }
379             $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;